#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <fcntl.h>

#include "main.h"
#include "log.h"

static log_target_t tgt = log_null;
static log_level_t lvl = log_debug;
static const void *arg;


static const char *
__buildstr(const char *__FILE, const uint16_t __LINE, log_level_t __lvl, const char *__str, char *__buf, const size_t __buflen)
{
  char loglevel_str[LOGLVL_STRLEN+1];

  memset(loglevel_str, 0, LOGLVL_STRLEN+1);
  switch (__lvl)
  {
    case log_err:
      strcpy(loglevel_str, "FAIL");
      break;
    case log_warn:
      strcpy(loglevel_str, "WARN");
      break;
    case log_not:
      strcpy(loglevel_str, "NULL");
      break;
    case log_info:
      strcpy(loglevel_str, "INFO");
      break;
    case log_debug:
      strcpy(loglevel_str, "DEBG");
      break;
  }
  snprintf(__buf, __buflen, "[%s] %s"
#ifdef ENABLE_DEBUG
                            ":%d"
#endif
                            "(%s.%d): %s\n", loglevel_str, PACKAGE_NAME,
#ifdef ENABLE_DEBUG
(pid_t) syscall (SYS_gettid),
#endif
                            __FILE, __LINE, __str);
  return (__buf);
}

void
log_to(const char *__FILE, const uint16_t __LINE, log_level_t __lvl, const char *__fmt, ...)
{
  char logstr[LOG_BUFLEN+1];
  va_list alist;

  if (__lvl < lvl) return;
  memset(logstr, 0, LOG_BUFLEN+1);
  va_start(alist, __fmt);
  switch (tgt)
  {
    case log_null:
    case log_stderr:
    case log_file:
      vfprintf(stderr, __buildstr(__FILE, __LINE, __lvl,__fmt, logstr, LOG_BUFLEN), alist);
      break;
    case log_syslog:
      break;
  }
  va_end(alist);
}

int
set_log_opts(const log_target_t __log_tgt, const log_level_t __log_lvl)
{
#ifndef ENABLE_SYSLOG
  if (__log_tgt == log_syslog)
  {
    E2("syslog is actually not supported");
    return (0);
  }
#endif
  tgt = __log_tgt;
  lvl = __log_lvl;
  return (1);
}

static int
__setup_logfd(const char *path, int mode)
{
  int logfd = open((char *)path, mode);
  if (logfd < 0) return (logfd);
  close(STDOUT_FILENO);
  close(STDERR_FILENO);
  dup2(logfd, STDOUT_FILENO);
  dup2(logfd, STDERR_FILENO);
  close(logfd);
  return (logfd);
}

int
log_init(const void *__log_arg)
{
  arg = __log_arg;
  switch (tgt)
  {
    case log_null:
    case log_stderr:
      break;
    case log_file:
      if (!arg) return (0);
      if (__setup_logfd((char *)arg, O_WRONLY | O_APPEND | O_CREAT) < 0)
      {
      }
      break;
    case log_syslog:
#ifdef ENABLE_SYSLOG
      openlog(PACKAGE_NAME, LOG_CONS | LOG_NDELAY, LOG_DAEMON);
#endif
      break;
  }

  return (1);
}

void
log_end(void)
{
  switch (tgt)
  {
    case log_null:
    case log_stderr:
    case log_file:
      close(STDOUT_FILENO);
      close(STDERR_FILENO);
      break;
    case log_syslog:
#ifdef ENABLE_SYSLOG
      closelog();
#endif
      break;
  }
}

